# Compiler project
# Gary Duncan and Sameer Sherwani

reductions = {} 

import copy
import functools, operator

class ST(dict):
	def addSymbol(self, symbol):

		if symbol.name in self.keys():
			print ST.MultiEx(symbol.name)
		self[symbol.name] = symbol
	
	def getSymbol(self, name):
		if name not in self.keys(): return None
		return self[name]
	
	class MultiEx(Exception):
		def __init__(self, symbol): self.symbol = symbol
		def __str__(self): return 'Symbol "%s" was declared in multiple locations' % self.symbol
	
class enterST:
	
	PROGRAM, PROCEDURE, LABEL, BOOLEAN, Integer, Real = range(6) # [1]
	typeStr = ["PROGRAM", "PROCEDURE", "LABEL", "BOOLEAN", "Integer", "Real"]
	
	SCALAR, VECTOR, MATRIX = range(3) # [2]
	shapeStr = ["SCALAR", "VECTOR", "MATRIX"]
	
	ROWS, COLS = range(2)
	
	def __init__(self, name, xtype, shape=None, dimr=None, dimc=None, **kwargs):
		self.name = name 
		self.xtype = xtype 
		self.shape = shape 
		self.dims = (dimr, dimc) 
		self.params = kwargs 
		self.__dict__.update(kwargs) 		
	
	def copy(self): return copy.deepcopy(self)
	
	def hDim(self): return self.dims[enterST.ROWS]
	def vDim(self): return self.dims[enterST.COLS]
	def getSize(self): return functools.reduce(operator.mul, (x for x in self.dims if x), 1)
	
	def __str__(self):
		datastr = enterST.typeStr[self.xtype] 
		if self.shape is not None: datastr += " " + enterST.shapeStr[self.shape]
		nonzerodims = [str(x) for x in self.dims if x] 
		if nonzerodims: datastr += " (%s elements)" % "x".join(nonzerodims)
		if len(self.params): datastr += " -- " + ", ".join(["%s=%s" % (k,v) for k,v in self.params.items()])
		return "[%s: %s]" % (self.name, datastr)
		
	@staticmethod
	def typeFromString(s):
		return dict((v, k) for k, v in enumerate(enterST.typeStr))[s]
		
	@staticmethod
	def fromTables(tables, name):
		for st in reversed(tables):
			e = st.getSymbol(name)
			if e is not None: return e
		return None

class STtype(int):
	def __str__(self): return enterST.typeStr[self]	

class fourTuple:
	def __init__(self, xtype, value=None, arg1=None, arg2=None):
		self.xtype, self.value = str(xtype), value
		self.arg1, self.arg2 = arg1, arg2
	def __str__(self):
		return '(%s, %s, %s, %s)' % (
			  self.value if self.value is not None else '-'
			, self.xtype if self.xtype is not None else '-'
			, self.arg1  if self.arg1  is not None else '-'
			, self.arg2  if self.arg2  is not None else '-')
	def copy(self): return copy.deepcopy(self)

#	start prog body End
def red1(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	globalst = symTbls.pop()
	if flag16:
		print("          Global ST is: ")
		for ste in globalst.values(): print("\t" + str(ste))
	yield fourTuple('PROGRAMEND', '')
reductions[1] = red1

#   prog Program var
def red2(parser, handle, instStackToken, symTbls,flag14, flag15, flag16):
	symTbls.append(ST()) 
	ste = enterST(handle[1].sem, enterST.PROGRAM)
	symTbls[0].addSymbol(ste); instStackToken.sem = ste
	yield fourTuple('PROGRAMBEGIN', ste.name)
reductions[2] = red2

#   declstat declstat string
def red8(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	ste = handle[0].sem.copy(); ste.name = handle[1].sem
	symTbls[-1].addSymbol(ste); instStackToken.sem = ste
	if flag15: print("          %s ST entry: %s" % 
		('GLOBAL' if len(symTbls) == 1 else 'LOCAL', ste))

	yield fourTuple('MEMORY', ste.name, ste.getSize(), STtype(ste.xtype))
reductions[8] = red8

#   declstat declstat , string = constant
def red9(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	ste = handle[0].sem.copy(); ste.name = handle[2].sem
	symTbls[-1].addSymbol(ste); instStackToken.sem = ste
	Parser.expectType(handle[4], ste.xtype, symTbls)
	if flag15: print("          %s ST entry: %s" % 
		('GLOBAL' if len(symTbls) == 1 else 'LOCAL', ste))
	yield fourTuple('MEMORY', ste.name, ste.getSize(), STtype(ste.xtype))
	yield fourTuple('MEMORYASSIGN', ste.name, 1, handle[4].sem)
reductions[9] = red9

#	declstat type IDENTIFIER
def red10(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	xtype = enterST.typeFromString(handle[0].sem)
	ste = enterST(handle[1].sem, xtype, enterST.SCALAR)
	symTbls[-1].addSymbol(ste); instStackToken.sem = ste
	if flag15: print("          %s ST entry: %s" % 
		('GLOBAL' if len(symTbls) == 1 else 'LOCAL', ste))
	yield fourTuple('MEMORY', ste.name, ste.getSize(), STtype(xtype))
reductions[10] = red10

#   declstat type vat = constant
def red11(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	xtype = enterST.typeFromString(handle[0].sem)
	ste = enterST(handle[1].sem, xtype, enterST.SCALAR)
	symTbls[-1].addSymbol(ste); instStackToken.sem = ste
	Parser.expectType(handle[3], xtype, symTbls)
	if flag15: print("          %s ST entry: %s" % 
		('GLOBAL' if len(symTbls) == 1 else 'LOCAL', ste))
	yield fourTuple('MEMORY', ste.name, ste.getSize(), STtype(xtype))
	yield fourTuple('MEMORYASSIGN', ste.name, 1, handle[3].sem)
reductions[11] = red11

#   declstat type var literal
def red12(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	xtype = enterST.typeFromString(handle[0].sem)
	dimr = int(handle[2].sem)	
	ste = enterST(handle[1].sem, xtype, enterST.VECTOR, dimr)
	symTbls[-1].addSymbol(ste); instStackToken.sem = ste	
	if flag16: print("          %s ST entry: %s" % 
		('GLOBAL' if len(symTbls) == 1 else 'LOCAL', ste))
	yield fourTuple('MEMORY', ste.name, ste.getSize(), STtype(xtype))
reductions[12] = red12

#   declstat type sting literal :: literal 
def red13(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	xtype = enterST.typeFromString(handle[0].sem)
	dimr, dimc = int(handle[2].sem), int(handle[4].sem)	
	ste = enterST(handle[1].sem, xtype, enterST.MATRIX, dimr, dimc)
	symTbls[-1].addSymbol(ste); instStackToken.sem = ste
	yield fourTuple('MEMORY', ste.name, ste.getSize(), STtype(xtype))
reductions[13] = red13

#type interger/real
def redtype(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	instStackToken.sem = handle[0].syn
reductions[14] = redtype
reductions[15] = redtype

#   proc prochead [declpart] statlist END
def redproc(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	oldlocalst = symTbls.pop()
	if flag14:
		print("          Local ST is: ")
		for ste in oldlocalst.values(): print("\t" + str(ste))
	yield fourTuple('ENDPROCEDURE', handle[0].sem)
reductions[19] = redproc
reductions[20] = redproc

#   prochead procname null-list/fparmlist
def redprochead(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	yield fourTuple('ENDFORMALPARAMETERLIST')
reductions[21] = redprochead
reductions[22] = redprochead

#procname Procedure var
def red23(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	ste = enterST(handle[1].sem, enterST.PROCEDURE)
	symTbls[0].addSymbol(ste)
	symTbls.append(ST())
	symTbls[-1].addSymbol(ste)
	instStackToken.sem = ste
	yield fourTuple('PROCEDUREBEGIN', ste.name)
reductions[23] = red23

# null-list
def red24(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	yield fourTuple('NOFORMALPARAMETERS')
reductions[24] = red24

# fparmlist fparmlist calltype var
def red25(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	xtype = enterST.typeFromString(handle[3].sem)
	ct = handle[2].sem
	ste = enterST(handle[4].sem, xtype, enterST.SCALAR, calltype = ct)
	symTbls[-1].addSymbol(ste); instStackToken.sem = ste
	yield fourTuple('FORMAL' + ct + 'PARAMETER', ste.name, ste.getSize(), STtype(xtype))
reductions[25] = red25

#fparmlist fparmlist calltype type var integer
def red26(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	xtype = enterST.typeFromString(handle[2].sem)
	dimr, ct = int(handle[4].sem), handle[1].sem
	ste = enterST(handle[3].sem, xtype, enterST.VECTOR, dimr, calltype = ct)
	symTbls[-1].addSymbol(ste); instStackToken.sem = ste
	yield fourTuple('FORMAL' + ct + 'PARAMETER', ste.name, ste.getSize(), STtype(xtype))
reductions[26] = red26

#   fparmlist fparmlist calltype type var integer :: integer
def red27(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	xtype = enterST.typeFromString(handle[2].sem)
	dimr, dimc, ct = int(handle[4].sem), int(handle[6].sem), handle[1].sem
	ste = enterST(handle[3].sem, xtype, enterST.MATRIX, dimr, dimc, calltype = ct)
	symTbls[-1].addSymbol(ste); instStackToken.sem = ste
	yield fourTuple('FORMAL' + ct + 'PARAMETER', ste.name, ste.getSize(), STtype(xtype))
reductions[27] = red27

#fparmlist calltype type var
def red28(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	xtype = enterST.typeFromString(handle[2].sem)
	ct = handle[1].sem
	ste = enterST(handle[3].sem, xtype, enterST.SCALAR, calltype = ct)
	symTbls[-1].addSymbol(ste); instStackToken.sem = ste
	yield fourTuple('BEGINFORMALPARAMETERLIST')
	yield fourTuple('FORMAL' + ct + 'PARAMETER', ste.name, ste.getSize(), STtype(xtype))
reductions[28] = red28

#   fparmlist calltype type var integer
def red29(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	xtype = enterST.typeFromString(handle[2].sem)
	dimr, ct = int(handle[4].sem), handle[1].sem
	ste = enterST(handle[3].sem, xtype, enterST.VECTOR, dimr, calltype = ct)
	symTbls[-1].addSymbol(ste); instStackToken.sem = ste
	yield fourTuple('BEGINFORMALPARAMETERLIST')
	yield fourTuple('FORMAL' + ct + 'PARAMETER', ste.name, ste.getSize(), STtype(xtype))
reductions[29] = red29

#   fparmlist calltype type vat integer :: integer
def red30(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	xtype = enterST.typeFromString(handle[2].sem)
	dimr, dimc, ct = int(handle[4].sem), int(handle[6].sem), handle[1].sem
	ste = enterST(handle[3].sem, xtype, enterST.MATRIX, dimr, dimc, calltype = ct)
	symTbls[-1].addSymbol(ste); instStackToken.sem = ste
	yield fourTuple('BEGINFORMALPARAMETERLIST')
	yield fourTuple('FORMAL' + ct + 'PARAMETER', ste.name, ste.getSize(), STtype(xtype))
reductions[30] = red30

#calltype value/reference
def redcalltype(parser, handle, instStackToken, symTbls, flag14, flag15, flag16):
	instStackToken.sem = handle[0].syn


